package com.xingfudazi.app.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xingfudazi.acl.cache.CacheClient;
import com.xingfudazi.app.domain.AppMember;
import com.xingfudazi.app.domain.AppTrade;
import com.xingfudazi.app.dto.SysAdjustEnergyDTO;
import com.xingfudazi.app.dto.TransferDTO;
import com.xingfudazi.app.enums.CurrencyEnum;
import com.xingfudazi.app.enums.TradeTypeEnum;
import com.xingfudazi.app.mapper.AppTradeMapper;
import com.xingfudazi.app.service.IAppMemberService;
import com.xingfudazi.app.service.IAppTradeService;
import com.xingfudazi.app.util.LockUtil;
import com.xingfudazi.common.exception.ExceptionEnum;
import com.xingfudazi.common.utils.SecurityUtils;
import com.xingfudazi.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Optional;

/**
 * <p>
 * 交易表 服务实现类
 * </p>
 *
 * @author Yu JiaMing
 * @since 2023-11-13
 */
@Service
public class AppTradeServiceImpl extends ServiceImpl<AppTradeMapper, AppTrade> implements IAppTradeService {

    @Autowired
    private IAppMemberService appMemberService;

    @Autowired
    private CacheClient cacheClient;


    @Override
    public IPage<AppTrade> findListByPage(Integer page, Integer pageCount) {
        IPage<AppTrade> wherePage = new Page<>(page, pageCount);
        AppTrade where = new AppTrade();
        return baseMapper.selectPage(wherePage, Wrappers.query(where));
    }

    @Override
    public int add(AppTrade appTrade) {
        return baseMapper.insert(appTrade);
    }

    @Override
    public int delete(Long id) {
        return baseMapper.deleteById(id);
    }

    @Override
    public int updateData(AppTrade appTrade) {
        return baseMapper.updateById(appTrade);
    }

    @Override
    public AppTrade findById(Long id) {
        return baseMapper.selectById(id);
    }

    @Override
    public boolean preEnergyValidate(Long nid, Integer costEnergy) {
        AppMember appMember = appMemberService.getById(nid);
        Integer energy = Optional.ofNullable(appMember.getGc()).orElse(0);
        return energy >= costEnergy;
    }

    @Override
    public List<AppTrade> findByOrderNo(String orderNo) {
        LambdaQueryWrapper<AppTrade> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AppTrade::getOrderNo, orderNo);
        wrapper.orderByDesc(AppTrade::getCreateTime);
        return list(wrapper);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void sysAdjustEnergy(SysAdjustEnergyDTO sysAdjustEnergyDTO) {
        Long nid = sysAdjustEnergyDTO.getNid();
        Integer energy = sysAdjustEnergyDTO.getGc();
        try {
            LockUtil.tradeLock(nid, 1000 * 30);
            AppMember appMember = appMemberService.getById(nid);
            Integer originEnergy = Optional.ofNullable(appMember.getGc()).orElse(0);

            if (originEnergy.equals(energy)) {
                return;
            }

            // 调整额度
            AppMember updateAppMember = new AppMember();
            updateAppMember.setId(nid);
            updateAppMember.setGc(energy);
            this.appMemberService.updateById(updateAppMember);

            // 添加交易记录
            AppTrade appTrade = new AppTrade();
            appTrade.setBValue(String.valueOf(originEnergy));
            appTrade.setAValue(String.valueOf(energy));
            appTrade.setWalletId(nid.toString());
            appTrade.setWalletName(appMember.getNickName());
            appTrade.setOrderNo(AppTrade.getNextSysOrderNo());
            appTrade.setCurrency(CurrencyEnum.ENERGY.name());
            appTrade.setPrice(String.valueOf(Math.abs(originEnergy - energy)));
            if (originEnergy > energy) {
                // 扣款
                appTrade.setType(TradeTypeEnum.SYS_ENERGY_SUB.name());
            } else {
                // 新增
                appTrade.setType(TradeTypeEnum.SYS_ENERGY_ADD.name());
            }
            appTrade.setRemark("系统调整");
            this.add(appTrade);

        } finally {
            LockUtil.tradeEnd(nid);
        }
    }

    @Override
    public List<AppTrade> list(AppTrade appTrade, Date currentTime) {
        LambdaQueryWrapper<AppTrade> wrapper = new LambdaQueryWrapper<>();
        if (currentTime != null) {
            wrapper.le(AppTrade::getCreateTime, currentTime);
        }
        String walletId = appTrade.getWalletId();
        String orderNo = appTrade.getOrderNo();

        if (StringUtils.isNotBlank(walletId)) {
            wrapper.eq(AppTrade::getWalletId, walletId);
        }

        if (StringUtils.isNotBlank(orderNo)) {
            wrapper.eq(AppTrade::getOrderNo, orderNo);
        }

        wrapper.orderByDesc(AppTrade::getCreateTime);

        return list(wrapper);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void transferEnergy(TransferDTO transferDTO) {
        Long toId = Long.valueOf(transferDTO.getTo());
        AppMember toMember = appMemberService.getById(toId);
        if (toMember == null) {
            throw ExceptionEnum.ILLEGAL_DATA.unwrap();
        }

        Integer value = transferDTO.getValue();
        String remark = StringUtils.isBlank(transferDTO.getRemark()) ? null : transferDTO.getRemark();
        Long nid = SecurityUtils.getUserId();
        // 预校验
        if (!preEnergyValidate(nid, value)) {
            throw ExceptionEnum.ENERGY_PRE_VALID_UN_PASS.unwrap();
        }

        // 开启转账
        try {
            LockUtil.tradeLock(nid, 1000 * 30);
            LockUtil.tradeLock(toId, 1000 * 30);

            // 二次校验
            AppMember fromMember = appMemberService.getById(nid);
            Integer energy = Optional.ofNullable(fromMember.getGc()).orElse(0);
            int ye = energy - value;
            if (ye < 0) {
                throw ExceptionEnum.ENERGY_PRE_VALID_UN_PASS.unwrap();
            }

            // 转账
            String orderNo = AppTrade.getNextTransferOrderNo();
            // 1、扣款
            AppMember updateAppMember = new AppMember();
            updateAppMember.setId(nid);
            updateAppMember.setGc(ye);
            this.appMemberService.updateById(updateAppMember);

            AppTrade appTrade = new AppTrade();
            appTrade.setBValue(String.valueOf(energy));
            appTrade.setAValue(String.valueOf(ye));
            appTrade.setWalletId(nid.toString());
            appTrade.setWalletName(fromMember.getNickName());
            appTrade.setOrderNo(orderNo);
            appTrade.setCurrency(CurrencyEnum.ENERGY.name());
            appTrade.setPrice(String.valueOf(value));
            appTrade.setType(TradeTypeEnum.TRANSFER_ENERGY_OUT.name());
            appTrade.setRemark(Optional.ofNullable(remark).orElse("转账" + value + "到" + toMember.getNickName()));
            this.add(appTrade);

            // 2.添加
            updateAppMember.setId(toId);
            updateAppMember.setGc(Optional.ofNullable(toMember.getGc()).orElse(0) + value);
            this.appMemberService.updateById(updateAppMember);

            appTrade = new AppTrade();
            Integer toEnergy = Optional.of(toMember.getGc()).orElse(0);
            appTrade.setBValue(String.valueOf(toEnergy));
            appTrade.setAValue(String.valueOf(toEnergy + value));
            appTrade.setWalletId(toMember.getId().toString());
            appTrade.setWalletName(toMember.getNickName());
            appTrade.setOrderNo(orderNo);
            appTrade.setCurrency(CurrencyEnum.ENERGY.name());
            appTrade.setPrice(String.valueOf(value));
            appTrade.setType(TradeTypeEnum.TRANSFER_ENERGY__IN.name());
            appTrade.setRemark(Optional.ofNullable(remark).orElse("收到来自" + fromMember.getNickName() + "的" + value + "茶能"));
            this.add(appTrade);

        } finally {
            LockUtil.tradeEnd(nid);
            LockUtil.tradeEnd(toId);
        }


    }

    public static void main(String[] args) {
        System.out.println(System.currentTimeMillis());
    }
}
